package com.yfp.client.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.github.binarywang.wxpay.bean.notify.WxPayPartnerNotifyV3Result;
import com.hy.corecode.idgen.WFGIdGenerator;
import com.yfp.client.domain.*;
import com.yfp.client.domain.*;
import com.yfp.client.domain.dto.PackSettleDTO;
import com.yfp.client.enums.CouponPackShopStatusEnum;
import com.yfp.client.enums.GoodsTypeEnum;
import com.yfp.client.enums.OrderStatus;
import com.yfp.client.service.*;
import com.yfp.client.service.*;
import com.yfp.client.util.RedisDistributedLock;
import com.yfp.client.util.ResponseEnum;
import com.yfp.client.util.SecurityUtils;
import com.yfp.common.core.constant.CacheConstants;
import com.yfp.common.core.enums.OrderTypeEnum;
import com.yfp.common.core.enums.PackageStatusEnums;
import com.yfp.common.core.enums.PayStatusEnum;
import com.yfp.common.core.enums.PayTypeEnum;
import com.yfp.common.core.exception.ServiceException;
import com.yfp.common.redis.service.RedisService;
import com.yfp.common.redis.utils.RedisDelayQueueUtil;
import com.yfp.operation.api.RemoteOperationService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.Asserts;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PackOrderServiceImpl implements PackOrderService {

    @Resource
    private WFGIdGenerator wFGIdGenerator;
    @Resource
    private MarCouponPackShopService marCouponPackShopService;
    @Resource
    private MarCouponPackService marCouponPackService;
    @Resource
    private ClientService clientService;
    @Resource
    private TagComboService tagComboService;
    @Resource
    private MachineService machineService;
    @Resource
    private AreaService areaService;
    @Resource
    private RouteService routeService;
    @Resource
    private PositionService positionService;
    @Resource
    private CompanyService companyService;
    @Resource
    private OrderComboService orderComboService;
    @Resource
    private OrderDetailService orderDetailService;
    @Resource
    private OrderListService orderListService;
    @Resource
    private PayService payService;
    @Resource
    private RedisService redisService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedisDelayQueueUtil redisDelayQueueUtil;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Autowired
    private ClientIntegralService clientIntegralService;
    @Resource
    private RemoteOperationService remoteOperationService;
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String settleCombo(PackSettleDTO packSettleDTO) {
        String clientId = SecurityUtils.getClientId();
        log.info("settleCombo,用户ID：{}", clientId);
        log.info("settleCombo,packSettleDTO:{}", packSettleDTO);
        // 小程序下单锁
        String lockKey = CacheConstants.MINAPP_ORDER_PACK_CREATE_LOCK_KEY + packSettleDTO.getComboDTOS().getId();
        RLock lock = redissonClient.getLock(lockKey);
        Assert.isTrue(lock.tryLock(), "稍后重试");

        try {
            // 校验传入数据
            Long packShopId = packSettleDTO.getComboDTOS().getId();
            this.checkSettleCombo(packShopId);

            // 创建订单
            String orderId = this.createSettleCombo(packSettleDTO);
            return orderId;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void checkSettleCombo(Long packShopId) {
        String clientId = SecurityUtils.getClientId();

        // 校验传入数据
        Assert.notNull(packShopId, "购买礼包不能为空");
        MarCouponPackShop marCouponPackShop = marCouponPackShopService.getById(packShopId);
        Assert.notNull(marCouponPackShop, "当前礼包已下架");
        Assert.isFalse(NumberUtil.equals(CouponPackShopStatusEnum.UNDER_SHELF.getCode(), marCouponPackShop.getStatus()), "当前礼包未上架");
        Assert.isTrue(NumberUtil.equals(CouponPackShopStatusEnum.ON_SHELF.getCode(), marCouponPackShop.getStatus()), "当前礼包已售罄");
        Assert.isFalse(NumberUtil.isLessOrEqual(NumberUtil.toBigDecimal(marCouponPackShop.getCurrentStock()), BigDecimal.ZERO), "当前礼包已售罄");

        // 获取礼包对应的优惠券
        Map<Long, List<MarCoupon>> MarCouponMapByCouponPackId = marCouponPackService.listCouponByCouponPackIds(Collections.singletonList(marCouponPackShop.getCouponPackId()));
        List<MarCoupon> marCoupons = MarCouponMapByCouponPackId.getOrDefault(marCouponPackShop.getCouponPackId(), new ArrayList<>());
        boolean canBuy = marCoupons.stream().allMatch(item -> {
            ResponseEnum responseEnum = marCouponPackShopService.verifyCouponBoolean(item.getId(), marCouponPackShop.getCouponPackId(), clientId);
            return ResponseEnum.OPERATOR_SUCCESS.getCode().equals(responseEnum.getCode());
        });
        Assert.isTrue(canBuy, "购买失败，您已经超过最大限购次数。");
    }

    @Override
    public String createSettleCombo(PackSettleDTO packSettleDTO) {
        String clientId = SecurityUtils.getClientId();
        String cityName = SecurityUtils.getCityName();
        DateTime date = DateUtil.date();

        Long packShopId = packSettleDTO.getComboDTOS().getId();
        MarCouponPackShop marCouponPackShop = marCouponPackShopService.getById(packShopId);

        // 获取设备信息
        Machine machine = machineService.findByMachineId(packSettleDTO.getMachineId());
        Area area = areaService.getById(machine.getAreaId());
        Route route = routeService.getById(machine.getRouteId());
        Position position = positionService.getById(machine.getPositionId());
        Company company = companyService.getById(machine.getCompanyId());
        // 获取礼包信息
        MarCouponPack marCouponPack = marCouponPackService.findById(marCouponPackShop.getCouponPackId());
        // 获取用户信息
        Client client = clientService.getById(clientId);
        // 获取标签名称
        TagCombo tagCombo = tagComboService.getById(marCouponPackShop.getTagId());


        // 封装订单主表数据
        OrderCombo orderCombo = new OrderCombo();
        orderCombo.setOrderId(String.valueOf(wFGIdGenerator.next()));
        orderCombo.setMachineId(machine.getMachineId());
        orderCombo.setMachineName(machine.getMachineName());
        orderCombo.setUserId(clientId);
        orderCombo.setStatus(OrderStatus.ToBePaid.getStatus());
        orderCombo.setReplacementState(0);
        orderCombo.setCupboardState(0);
        orderCombo.setScannedStatus(0);
        orderCombo.setCreateTime(date.toTimestamp());
        orderCombo.setUpdateTime(date);
        orderCombo.setPrice(marCouponPackShop.getPrice().toString());
        orderCombo.setTotalPrice(marCouponPackShop.getPrice());
        orderCombo.setAppDiscountMoney(BigDecimal.ZERO);
        orderCombo.setCouponDiscountMoney(BigDecimal.ZERO);
        orderCombo.setGoodsType(GoodsTypeEnum.VIRTUAL.getCode());
        orderCombo.setCityName(cityName);

        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setOrderDetailId(String.valueOf(wFGIdGenerator.next()));
        orderDetail.setOrderId(orderCombo.getOrderId());
        orderDetail.setPackageId(marCouponPackShop.getId().toString());
        orderDetail.setPackageName(marCouponPack.getPackName());
        orderDetail.setIsHeatAdditive(true);
        orderDetail.setHeatTime(0L);
        orderDetail.setStatus(PackageStatusEnums.PendingPayment.getStatus());
        orderDetail.setApplietAmount(marCouponPackShop.getPrice().toString());
        orderDetail.setDeviceAmount(marCouponPackShop.getPrice().toString());
        orderDetail.setAmount(marCouponPackShop.getPrice().toString());
        orderDetail.setThumbUrl(marCouponPackShop.getShopImg());
        orderDetail.setCompletedCount(0L);
        orderDetail.setPickedUpCount(0L);
        orderDetail.setCount(1L);
        orderDetail.setScannedStatus(0);
        orderDetail.setReplacementState(0);
        orderDetail.setCreateTime(date);
        orderDetail.setUpdateTime(date);
        orderDetail.setCityName(cityName);

        OrderList orderList = new OrderList();
        orderList.setId(orderDetail.getOrderDetailId());
        orderList.setOrderSn(orderCombo.getOrderId());
        orderList.setOrderTime(date);
        orderList.setUserName(client.getNickName());
        orderList.setUserMobile(client.getMobile());
        orderList.setAmt(orderDetail.getAmount().toString());
        orderList.setOriginalPrice(new BigDecimal(orderDetail.getApplietAmount()));
        orderList.setPayType(PayTypeEnum.WeChatPay.getCode().toString());
        orderList.setPayStatus(PayStatusEnum.UNPAID.getCode());
        orderList.setComboId(orderDetail.getPackageId());
        orderList.setComboName(orderDetail.getPackageName());
        orderList.setComboTag(tagCombo.getTagName());
        orderList.setMachineId(orderCombo.getMachineId());
        orderList.setCompanyId(machine.getCompanyId());
        orderList.setCompanyName(company.getName());
        orderList.setAreaId(machine.getAreaId());
        orderList.setAreaName(area.getName());
        orderList.setRouteId(machine.getRouteId());
        orderList.setRouteName(route.getName());
        orderList.setPositionId(machine.getPositionId());
        orderList.setPositionName(position.getPositonName());
        orderList.setPositionAddr(machine.getPositionAddr());
        orderList.setIsException("1");
        orderList.setOrderType(OrderTypeEnum.WeChat.getCode().toString());
        orderList.setUpdateTime(date);
        orderList.setUpdateName(client.getNickName());
        orderList.setCount(orderDetail.getCount());
        orderList.setGoodsType(GoodsTypeEnum.VIRTUAL.getCode());
        orderList.setCityName(cityName);

        // 更新订单数据
        orderComboService.save(orderCombo);
        orderDetailService.save(orderDetail);
        orderListService.save(orderList);

        // 更新库存-减
        marCouponPackShopService.decreaseStockQuantity(packSettleDTO.getComboDTOS());

        // 将订单信息加入到延迟队列。如果超时没有支付，取消订单，恢复库存
        String jsonStr = JSONUtil.toJsonStr(orderCombo);
        redisDelayQueueUtil.addDelayQueue(jsonStr, 3, TimeUnit.MINUTES, CacheConstants.MINAPP_ORDER_PACK_CREATE_DELAY_QUEUE_KEY);
        return orderCombo.getOrderId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancelOrder(String orderId) {
        return this.cancelOrder(orderId, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancelOrder(String orderId, String reason) {
        OrderCombo orderCombo = orderComboService.getById(orderId);
        if (orderCombo == null) {
            return false;
        }
        // 制作状态检测
        if (!StrUtil.equals(orderCombo.getStatus(), OrderStatus.ToBePaid.getStatus())) {
            return false;
        }

        // 微信关闭订单
        payService.closeOrder(orderId);
        redisService.deleteObject(CacheConstants.MINAPP_ORDER_CREATE_WX_PAY_PARTNER_JSAPI_KEY + orderId);

        // 修改订单状态
        DateTime date = DateUtil.date();
        orderCombo.setReason(reason);
        orderCombo.setStatus(OrderStatus.Canceled.getStatus());
        orderCombo.setUpdateTime(date);
        orderComboService.updateById(orderCombo);
        // 修改订单状态
        List<OrderList> orderLists = orderListService.listByOrderSn(orderId);
        orderLists.forEach(item -> {
            item.setPayStatus(PayStatusEnum.CANCELLED.getCode());
            item.setUpdateTime(date);
            item.setRefundReason(reason);
        });
        orderListService.updateBatchById(orderLists);

        // 更新库存-加
        List<OrderDetail> orderDetails = orderDetailService.listByOrderId(orderId);
        marCouponPackShopService.recoverStockQuantity(orderCombo, orderDetails);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderCreateNotify(WxPayPartnerNotifyV3Result wxPayPartnerNotifyV3Result) {
        String orderId = wxPayPartnerNotifyV3Result.getResult().getOutTradeNo();
        RedisDistributedLock redisDistributedLock = new RedisDistributedLock(redisTemplate, "client:wxpay:orderCreateNotify:" + orderId, 10);
        try {
            // 加锁
            redisDistributedLock.lock();

            OrderCombo orderCombo = orderComboService.getById(orderId);

            if (OrderStatus.AwaitPackage.getStatus().equals(orderCombo.getStatus())) {
                return;
            }
            List<OrderList> orderLists = orderListService.listByOrderSn(orderId);
            OrderList first = CollUtil.getFirst(orderLists);
            if (!PayStatusEnum.UNPAID.getCode().equals(first.getPayStatus())) {
                return;
            }

            List<OrderDetail> orderDetails = orderDetailService.listByOrderId(orderId);
            Client client = clientService.getById(orderCombo.getUserId());

            Date date = new Date();
            // 修改订单状态
            orderCombo.setStatus(OrderStatus.Finish.getStatus());
            orderCombo.setUpdateTime(date);

            orderDetails.forEach(item -> {
                item.setStatus(PackageStatusEnums.PickupIsComplete.getStatus());
                item.setUpdateTime(date);
            });

            orderLists.forEach(item -> {
                item.setPayTradeNo(wxPayPartnerNotifyV3Result.getResult().getTransactionId());
                item.setPayStatus(PayStatusEnum.COMPLETED.getCode());
                item.setPayTime(DateUtil.parseUTC(wxPayPartnerNotifyV3Result.getResult().getSuccessTime()).toTimestamp());
                item.setUpdateTime(date);
            });

            // 事物提交
            Boolean execute = transactionTemplate.execute(e -> {
                orderComboService.updateById(orderCombo);
                orderDetailService.updateBatchById(orderDetails);
                orderListService.updateBatchById(orderLists);
                return Boolean.TRUE;
            });
            if (!execute) {
                log.error("订单支付成功,订单号:{},修改订单状态失败", orderId);
                return;
            }
            log.info("订单支付成功,订单号:{},修改订单状态成功", orderId);
            // 不参与分账
            // remoteOperationService.orderRouting(orderId, PayTypeEnum.WeChatPay.getCode());

            // 发放优惠券
            List<Long> packShopIds = orderDetails.stream().map(item -> Long.parseLong(item.getPackageId())).collect(Collectors.toList());
            Long packShopId = CollUtil.getFirst(packShopIds);
            marCouponPackShopService.giveCoupon(orderCombo.getUserId(), packShopId);
            // 增加售出数量
            marCouponPackShopService.addSoldQuantity(packShopId);
            // 当实时库存为0时，更新礼包状态为已售完
            marCouponPackShopService.updateStatusToSoldOut(packShopId);
        } catch (Exception e) {
            log.info("orderCreateNotify:出现异常， 异常信息：{}", e.getMessage());
        } finally {
            redisDistributedLock.unlock();
        }
    }

    @Override
    public Boolean settleComboCheck(String orderId) {
        List<OrderList> orderLists = orderListService.listByOrderSn(orderId);
        OrderList orderList = CollUtil.getFirst(orderLists);
        Asserts.notNull(orderList, "订单不存在");
        // 只针对礼包
        if (GoodsTypeEnum.ENTITY.getCode().equals(orderList.getGoodsType())) {
            return Boolean.TRUE;
        }

        OrderCombo orderCombo = orderComboService.getById(orderId);
        if (OrderStatus.Canceled.getStatus().equals(orderCombo.getStatus())) {
            return Boolean.FALSE;
        }

        MarCouponPackShop marCouponPackShop = marCouponPackShopService.getById(Long.parseLong(orderList.getComboId()));
        try {
            String clientId = SecurityUtils.getClientId();

            Assert.notNull(marCouponPackShop, "当前礼包已下架");
            Assert.isFalse(NumberUtil.equals(CouponPackShopStatusEnum.DELETE.getCode(), marCouponPackShop.getStatus()), "当前礼包已下架");
            Assert.isFalse(NumberUtil.equals(CouponPackShopStatusEnum.UNDER_SHELF.getCode(), marCouponPackShop.getStatus()), "当前礼包待上架");
            Assert.isFalse(NumberUtil.equals(CouponPackShopStatusEnum.OFF_SHELF.getCode(), marCouponPackShop.getStatus()), "当前礼包已下架");

            // 获取礼包对应的优惠券
            Map<Long, List<MarCoupon>> MarCouponMapByCouponPackId = marCouponPackService.listCouponByCouponPackIds(Collections.singletonList(marCouponPackShop.getCouponPackId()));
            List<MarCoupon> marCoupons = MarCouponMapByCouponPackId.getOrDefault(marCouponPackShop.getCouponPackId(), new ArrayList<>());
            for (MarCoupon marCoupon : marCoupons) {
                ResponseEnum responseEnum = marCouponPackShopService.verifyCouponBoolean(marCoupon.getId(), marCouponPackShop.getCouponPackId(), clientId);
                Assert.isTrue(ResponseEnum.OPERATOR_SUCCESS.getCode().equals(responseEnum.getCode()), "购买失败，您已经超过最大限购次数。");
            }
        } catch (Exception e) {
            // 如果优惠券不可用，关闭微信订单
            payService.closeOrder(orderId);
            throw new ServiceException(e.getMessage());
        }
        return Boolean.TRUE;
    }
}
